package yxy.cherry.game.base.bean;

import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RMap;

import yxy.apple.util.redisson.RedisConnection;
import yxy.apple.util.redisson.RedisException;

public class GatewayBean {
	public static final String RedisKeySharedGatewayBeanPrefix = "gw_";
	public static final String RedisKeySharedGatewayBeanLockPrefix = "gw_l_";
	public static final String RedisKeySharedGatewayBeanMap = "gwm";

	// bean===========================================================================

	private int system = 0;
	private int cluster = 0;
	private int channel = 0;
	private int uid = 0;
	private int sid = 0;
	private String pcode = "";

	private long loginTime = 0;

	public int getSystem() {
		return system;
	}

	public void setSystem(int system) {
		this.system = system;
	}

	public int getCluster() {
		return cluster;
	}

	public void setCluster(int cluster) {
		this.cluster = cluster;
	}

	public int getChannel() {
		return channel;
	}

	public void setChannel(int channel) {
		this.channel = channel;
	}

	public int getUid() {
		return uid;
	}

	public void setUid(int uid) {
		this.uid = uid;
	}

	public int getSid() {
		return sid;
	}

	public void setSid(int sid) {
		this.sid = sid;
	}

	public String getPcode() {
		return pcode;
	}

	public void setPcode(String pcode) {
		this.pcode = pcode;
	}

	public long getLoginTime() {
		return loginTime;
	}

	public void setLoginTime(long loginTime) {
		this.loginTime = loginTime;
	}

	// redis==========================================================================

	private static String key(int uid) {
		return GatewayBean.RedisKeySharedGatewayBeanPrefix + uid;
	}

	private static RBucket<GatewayBean> touch(RedisConnection redis, int uid) throws RedisException {
		if (redis.ready()) {
			try {
				return redis.client().getBucket(key(uid));
			} catch (Exception e) {
				throw new RedisException(e);
			}
		} else {
			throw new RedisException("无可用Redis连接!");
		}
	}

	private static RMap<String, GatewayBean> touchMap(RedisConnection redis) throws RedisException {
		if (redis.ready()) {
			try {
				return redis.client().getMap(GatewayBean.RedisKeySharedGatewayBeanMap);
			} catch (Exception e) {
				throw new RedisException(e);
			}
		} else {
			throw new RedisException("无可用Redis连接!");
		}
	}

	public static Collection<GatewayBean> all(RedisConnection redis) {
		try {
			return touchMap(redis).readAllValues();
		} catch (RedisException e) {
			e.printStackTrace();
		}
		return new ArrayList<>();
	}

	/**
	 * 查找一个GatewayBean
	 * 
	 * @param redis
	 * @param uid
	 * @return
	 * @throws RedisException
	 */
	public static GatewayBean find(RedisConnection redis, int uid) throws RedisException {
		RBucket<GatewayBean> bucket = touch(redis, uid);
		return bucket.get();
	}

	/**
	 * 缓存一个GatewayBean，永不超时
	 * 
	 * @param redis
	 * @param uid
	 * @param bean
	 * @throws RedisException
	 */
//	public static void put(RedisConnection redis, int uid, GatewayBean bean) throws RedisException {
//		GatewayBean.put(redis, uid, bean, 0);
//	}

	/**
	 * 缓存一个GatewayBean，并自动超时。
	 * 
	 * @param redis
	 * @param uid
	 * @param bean
	 * @param expiration 毫秒单位
	 * @throws Exception
	 */
	public static void put(RedisConnection redis, int uid, GatewayBean bean, long expiration) throws Exception {
		RLock lockBean = lockBean(redis, uid);
		RLock lockBeanMap = lockBeanMap(redis);
		try {
			if (tryLock(lockBean) && tryLock(lockBeanMap)) {
				RBucket<GatewayBean> bucket = touch(redis, uid);
				bucket.set(bean);
				if (expiration > 0) {
					bucket.expire(expiration, TimeUnit.MILLISECONDS);
				}
				touchMap(redis).put(key(uid), bean);
			}
		} catch (Exception e) {
			throw e;
		} finally {
			unlock(lockBean);
			unlock(lockBeanMap);
		}
	}

	/**
	 * 移除一个缓存的GatewayBean，要求uid, system, cluster都对应上的才移除。
	 * 
	 * @param redis
	 * @param uid
	 * @param cluster
	 * @param cluster
	 * @return
	 * @throws Exception
	 */
	public static GatewayBean remove(RedisConnection redis, int uid, int system, int cluster, int channel) throws Exception {
		RLock lockBean = lockBean(redis, uid);
		RLock lockBeanMap = lockBeanMap(redis);
		try {
			if (tryLock(lockBean) && tryLock(lockBeanMap)) {
				RBucket<GatewayBean> bucket = touch(redis, uid);
				GatewayBean bean = bucket.get();
				if (bean != null && bean.system == system && bean.cluster == cluster && bean.channel == channel) {
					bucket.delete();
					touchMap(redis).remove(key(uid));
					return bean;
				}
			}
		} catch (Exception e) {
			throw e;
		} finally {
			unlock(lockBean);
			unlock(lockBeanMap);
		}
		return null;
	}

	/**
	 * 续订已缓存GatewayBean的过期时间，要求uid, system, cluster都对应上的才能续订
	 * 
	 * @param redis
	 * @param uid
	 * @param cluster
	 * @param expiration
	 * @return
	 * @throws RedisException
	 */
	public static GatewayBean renew(RedisConnection redis, int uid, int system, int cluster, long expiration) throws Exception {
		RLock lockBean = lockBean(redis, uid);
		try {
			if (tryLock(lockBean)) {
				RBucket<GatewayBean> bucket = touch(redis, uid);
				GatewayBean bean = bucket.get();
				if (expiration > 0 && bean != null && bean.system == system && bean.cluster == cluster) {
					bucket.expire(expiration, TimeUnit.MILLISECONDS);
				}
				return bean;
			}
		} catch (Exception e) {
			throw e;
		} finally {
			unlock(lockBean);
		}
		return null;
	}

	public static void refreshBeanMap(RedisConnection redis) throws Exception {
		RLock lockBeanMap = lockBeanMap(redis);
		try {
			if (tryLock(lockBeanMap)) {
				RMap<String, GatewayBean> map = touchMap(redis);
				Collection<GatewayBean> all = map.readAllValues();
				for (GatewayBean bean : all) {
					if (!touch(redis, bean.getUid()).isExists()) {
						map.remove(key(bean.getUid()));
					}
				}
			}
		} catch (Exception e) {
			throw e;
		} finally {
			unlock(lockBeanMap);
		}
	}

	/**
	 * 
	 * @param redis
	 * @param uid
	 * @return
	 */
//	public static RLock lock(RedisConnection redis, int uid) {
//		return GatewayBean.lock(redis, uid, 0);
//	}

	/**
	 * 锁定uid所在网关信息
	 * 
	 * @param redis
	 * @param uid
	 * @param duration 单位毫秒
	 * @return
	 */
//	public static RLock lock(RedisConnection redis, int uid, int duration) {
//		RLock locker = redis.client().getLock(GatewayBean.RedisKeySharedGatewayBeanLockPrefix + uid);
//		if (duration > 0) {
//			locker.lock(duration, TimeUnit.MILLISECONDS);
//		} else {
//			locker.lock();
//		}
//		return locker;
//	}

	private static RLock lockBean(RedisConnection redis, int uid) {
		return redis.client().getLock(GatewayBean.RedisKeySharedGatewayBeanLockPrefix + uid);
	}

	private static RLock lockBeanMap(RedisConnection redis) {
		return redis.client().getLock(GatewayBean.RedisKeySharedGatewayBeanMap + "_l");
	}

	private static boolean tryLock(RLock locker) throws InterruptedException {
		return locker.tryLock(10, 5, TimeUnit.SECONDS);
	}

	public static void unlock(RLock locker) {
		locker.unlock();
	}

	public String toString() {
		return String.format("{class:\"%s\", system:%d, cluster:%d, channel:%d}", this.getClass().getName(), this.system, this.cluster, this.channel);
	}
}
